import { render } from "../index";

export let lastState; // 上次数据
export const hookStates = []; // 保存所有状态变量的数组
export const hook = { index: 0 }; // 存储的下标(默认是从第一个开始)

//! 存在的问题是定义多个变量则共用一个变量
//! 优化的地方则是使用一个栈进行维护不同变量
/**
 * 基础版 useState 实现
 * @param {*} initialstate
 * @returns
 */
export function useBaseState(initialstate) {
    lastState = lastState || initialstate;
    function setState(newState) {
        lastState = newState;
        // 重新刷新界面
        render();
    }
    // 注意这里返回出去一定是数组，只有数组解构才能设置不同变量
    return [lastState, setState];
}

//! 解决了只能单个变量进行记录
/**
 * useState
 * @param {*} initialstate
 * @returns
 */
export function useState(initialstate) {
    hookStates[hook.index] = hookStates[hook.index] || initialstate; // 以前有值拿以前值否则将当前状态的值保存在数组
    // 保留一份上次 hookIndex, 保证 setState 拿到自己索引
    let currentIndex = hook.index;
    function setState(newState) {
        hookStates[currentIndex] = newState; // 找到对应的数据进行改变 (使用了闭包)
        render();
    }
    return [hookStates[hook.index++], setState]; // 注意返回出去的值 hookIndex 当时还没有 ++
}

// --------------------------------------------------------
// useMemo useCallback 优化 ，防止数据不变重新渲染的问题
// --------------------------------------------------------

//! useMemo 传入的参数第一个函数返回的是对象 (所以 useMemo 一般使用缓存数据对象)
/**
 * useMemo
 * @param {*} factory
 * @param {*} dependencies
 * @returns
 */
export function useMemo(factory, dependencies) {
    // 判断是否已经存有值
    if (hookStates[hook.index]) {
        let [lastMemo, lastDependencies] = hookStates[hook.index]; // 获取缓存过对象
        // 比较 dependencies 和 lastDependencies新旧值进行对比查看有没变化，如果没有变化则返回旧值
        // 如果有变化，则产生最新对象缓存
        let same = dependencies.every(
            (item, index) => item === lastDependencies[index]
        );
        // 已缓存
        if (same) {
            hook.index++;
            return lastMemo;
        } else {
            // 没缓存
            let newMemo = factory(); // 获取需要缓存对象
            hookStates[hook.index++] = [newMemo, dependencies]; // 进行缓存处理
            return newMemo;
        }
    } else {
        // 没有缓存过对象(第一次必定进入这里)
        let newMemo = factory(); // 获取需要缓存对象
        hookStates[hook.index++] = [newMemo, dependencies]; // 进行缓存处理(往缓存栈中后面放入)
        return newMemo;
    }
}

//! useCallBack 传入的参数第一个是函数返回的是函数  (所以 useCallBack 一般用于缓存方法)
/**
 * useCallBack
 * @param {*} callback
 * @param {*} dependencies
 * @returns
 */
export function useCallback(callback, dependencies) {
    console.log(hookStates);
    // 也是先判断是否有值
    if (hookStates[hook.index]) {
        let [lastCallback, lastDependencies] = hookStates[hook.index];
        //! 注意这里一般依赖项值都是容易改变的
        let same = dependencies.every(
            (item, index) => lastDependencies[index] === item
        );
        // 依赖缓存是否改变
        if (same) {
            hook.index++;
            return lastCallback; // 直接返回已经旧的回调函数
        } else {
            hookStates[hook.index++] = [callback, dependencies];
            return callback;
        }
    } else {
        // 没有缓存过
        hookStates[hook.index++] = [callback, dependencies];
        return callback; // 此时直接返回下次就可以直接执行
    }
}

//! useEffect 实现跟 useCallBack 有点类似， 但是没有返回值的
/**
 * useEffect
 * @param {*} callback
 * @param {*} dependencies
 */
export function useEffect(callback, dependencies) {
    if (hookStates[hook.index]) {
        // 获取老的缓存函数返回值已经依赖项
        let [lastDestroy, lastDependencies] = hookStates[hook.index];
        let same = false;
        // 判断是否写过依赖项没有则是 undefined
        if (lastDependencies) {
            same = dependencies.every(
                (item, index) => item === lastDependencies[index]
            );
        }
        if (same) {
            hook.index++;
        } else {
            lastDestroy && lastDestroy(); // 可能没有返回值, 如果有返回值先调用上次的回调函数
            const arr = [dependencies]; // 执行函数获取到新的函数返回值
            setTimeout(() => {
                const destroy = callback(); // 在宏任务中执行钩子函数
                arr.unshift(destroy);
            });
            hookStates[hook.index++] = arr;
        }
    } else {
        const arr = [dependencies]; // 第一次也需要执行，获取函数返回的值
        setTimeout(() => {
            const destroy = callback();
            arr.unshift(destroy);
        });
        hookStates[hook.index++] = arr;
    }
}

//! useLayoutEffect 实现跟 useEffect 差不多，只不过函数执行一个在红任务和在微任务执行的区别
/**
 * useLayoutEffect
 * @param {*} callback
 * @param {*} dependencies
 */
export function useLayoutEffect(callback, dependencies) {
    console.log(hookStates);
    if (hookStates[hook.index]) {
        let { lastDestroy, lastDependencies } = hookStates[hook.index];
        let same = false;
        if (lastDependencies) {
            same = dependencies.every(
                (item, index) => item === lastDependencies[index]
            );
        }
        if (same) {
            hook.index++;
        } else {
            lastDestroy && lastDestroy(); // 先销毁上次执行返回函数
            const arr = [dependencies];
            queueMicrotask(() => {
                const destroy = callback();
                arr.unshift(destroy);
            });
            hookStates[hook.index++] = arr;
        }
    } else {
        const arr = [dependencies];
        // 进行微任务将 callback 进行执行
        queueMicrotask(() => {
            const destroy = callback();
            arr.unshift(destroy);
        });
        hookStates[hook.index++] = arr;
    }
}

//! useRef 绑定 dom 元素
/**
 * useRef
 * @param {*} initialState
 * @returns
 */
export function useRef(initialState) {
    hookStates[hook.index] = hookStates[hook.index] || {
        current: initialState,
    };
    return hookStates[hook.index++];
}

//! useReducer 实现类似于 useState
export function useReducer(reducer, initialState) {
    hookStates[hook.index] = hookStates[hook.index] || initialState;
    let current = hook.index;
    function dispatch(action) {
        // 基于一个函数来实现状态初始化
        hookStates[current] = reducer
            ? reducer(hookStates[current], action) // 传递数据及状态
            : action;
        render(); // 派发后重新渲染
    }
    // 返回基础数据以及派发函数
    return [hookStates[hook.index++], dispatch];
}

//!  useHighState 是通过  useReducer  封装而来
export function useHighState(initialState) {
    return useReducer(null, initialState);
}

/**
 * useContext
 * @param {*} context
 * @returns
 */
export function useContext(context) {
    // 内部使用 Provider 的时候 会将提供的数据 挂在到上下文组件上
    return context._currentValue;
}

//! 用于父组件获取子组件 dom 及事件
/**
 * useInperativeHandle
 * @param {*} ref
 * @param {*} handle
 */
export function useImperativeHandle(ref, handle) {
    ref.current = handle();
}
